/**
 * @file   AbstractClient.cpp
 * @author Djuro Drljaca (djurodrljaca@gmail.com)
 * @date   2013-05-18
 * @brief  DataBus Abstract Client.
 *
 * Copyright (C) 2013  Djuro Drljaca <djurodrljaca@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <DataBus/Client/AbstractClient.h>
#include <DataBus/Common/PingResponsePacket.h>
#include <DataBus/Common/RegisterClientRequestPacket.h>
#include <DataBus/Common/RegisterClientResponsePacket.h>
#include <DataBus/Common/GetClientListRequestPacket.h>
#include <DataBus/Common/GetClientNameRequestPacket.h>
#include <DataBus/Common/GetClientNameResponsePacket.h>
#include <DataBus/Common/GetClientStatusRequestPacket.h>
#include <DataBus/Common/GetClientPropertyListRequestPacket.h>
#include <DataBus/Common/GetClientPropertyListResponsePacket.h>
#include <DataBus/Common/GetClientPropertyInfoRequestPacket.h>
#include <DataBus/Common/GetClientPropertyInfoResponsePacket.h>
#include <DataBus/Common/GetClientPropertyValueRequestPacket.h>
#include <DataBus/Common/GetClientPropertyValueResponsePacket.h>
#include <DataBus/Common/SetClientPropertyValueRequestPacket.h>
#include <DataBus/Common/SetClientPropertyValueResponsePacket.h>
#include <DataBus/Common/GetClientMethodListRequestPacket.h>
#include <DataBus/Common/GetClientMethodListResponsePacket.h>
#include <DataBus/Common/GetClientMethodInfoRequestPacket.h>
#include <DataBus/Common/GetClientMethodInfoResponsePacket.h>
#include <DataBus/Common/GetClientMethodParameterInfoRequestPacket.h>
#include <DataBus/Common/GetClientMethodParameterInfoResponsePacket.h>
#include <DataBus/Common/GetClientMethodReturnValueInfoRequestPacket.h>
#include <DataBus/Common/GetClientMethodReturnValueInfoResponsePacket.h>

DataBus::AbstractClient::AbstractClient(QObject *parent)
    : QObject(parent),
      m_clientId(0),
      m_clientName(),
      m_activityTimeout(0),
      m_pingResponseTimeout(0),
      m_pingRetryCount(0),
      m_packetParser(),
      m_packetIdCounter(0),
      m_serverTimeout(0),
      m_serverTimeoutTimer(),
      m_timer(),
      m_registered(false)
{
}

DataBus::AbstractClient::~AbstractClient()
{
    // Stop client
    stop();
}


quint8 DataBus::AbstractClient::getClientId() const
{
    return m_clientId;
}

QString DataBus::AbstractClient::getClientName() const
{
    return m_clientName;
}

quint8 DataBus::AbstractClient::getActivityTimeout() const
{
    return m_activityTimeout;
}

quint8 DataBus::AbstractClient::getPingResponseTimeout() const
{
    return m_pingResponseTimeout;
}

quint8 DataBus::AbstractClient::getPingRetryCount() const
{
    return m_pingRetryCount;
}

bool DataBus::AbstractClient::isRegistered() const
{
    return m_registered;
}


bool DataBus::AbstractClient::sendGetClientListRequestPacket()
{
    // Create packet
    Packet packet;

    if (GetClientListRequestPacket::create(m_clientId,
                                           getNextPacketId(),
                                           &packet) == false)
    {
        // Error, failed to create packet
        return false;
    }

    // Send packet
    if (sendPacket(packet) == false)
    {
        // Error, failed to send packet
        return false;
    }

    // Success
    return true;
}

bool DataBus::AbstractClient::sendGetClientNameRequestPacket(const quint8 clientId)
{
    // Create packet
    Packet packet;

    if (GetClientNameRequestPacket::create(m_clientId, clientId, getNextPacketId(), &packet) == false)
    {
        // Error, failed to create packet
        return false;
    }

    // Send packet
    if (sendPacket(packet) == false)
    {
        // Error, failed to send packet
        return false;
    }

    // Success
    return true;
}

bool DataBus::AbstractClient::sendGetClientStatusRequestPacket(const quint8 clientId)
{
    // Create packet
    Packet packet;

    if (GetClientStatusRequestPacket::create(m_clientId, getNextPacketId(), clientId, &packet) == false)
    {
        // Error, failed to create packet
        return false;
    }

    // Send packet
    if (sendPacket(packet) == false)
    {
        // Error, failed to send packet
        return false;
    }

    // Success
    return true;
}

bool DataBus::AbstractClient::sendGetClientPropertyListRequestPacket(const quint8 clientId)
{
    // Create packet
    Packet packet;

    if (GetClientPropertyListRequestPacket::create(m_clientId, clientId, getNextPacketId(), &packet) == false)
    {
        // Error, failed to create packet
        return false;
    }

    // Send packet
    if (sendPacket(packet) == false)
    {
        // Error, failed to send packet
        return false;
    }

    // Success
    return true;
}

bool DataBus::AbstractClient::sendGetClientPropertyInfoRequestPacket(const quint8 clientId,
                                                                     const quint8 propertyId)
{
    // Create packet
    Packet packet;

    if (GetClientPropertyInfoRequestPacket::create(m_clientId,
                                                   clientId,
                                                   getNextPacketId(),
                                                   propertyId,
                                                   &packet) == false)
    {
        // Error, failed to create packet
        return false;
    }

    // Send packet
    if (sendPacket(packet) == false)
    {
        // Error, failed to send packet
        return false;
    }

    // Success
    return true;
}

bool DataBus::AbstractClient::sendGetClientPropertyValueRequestPacket(const quint8 clientId,
                                                                      const quint8 propertyId)
{
    // Create packet
    Packet packet;

    if (GetClientPropertyValueRequestPacket::create(m_clientId,
                                                    clientId,
                                                    getNextPacketId(),
                                                    propertyId,
                                                    &packet) == false)
    {
        // Error, failed to create packet
        return false;
    }

    // Send packet
    if (sendPacket(packet) == false)
    {
        // Error, failed to send packet
        return false;
    }

    // Success
    return true;
}

bool DataBus::AbstractClient::sendGetClientPropertyValueResponsePacket(const quint8 clientId,
                                                                       const quint8 packetId,
                                                                       const quint8 propertyId,
                                                                       const Value &value)
{
    // Create packet
    Packet packet;

    if (GetClientPropertyValueResponsePacket::create(m_clientId,
                                                     clientId,
                                                     packetId,
                                                     propertyId,
                                                     value,
                                                     &packet) == false)
    {
        // Error, failed to create packet
        return false;
    }

    // Send packet
    if (sendPacket(packet) == false)
    {
        // Error, failed to send packet
        return false;
    }

    // Success
    return true;
}

bool DataBus::AbstractClient::sendSetClientPropertyValueRequestPacket(const quint8 clientId,
                                                                      const quint8 propertyId,
                                                                      const Value &value)
{
    // Create packet
    Packet packet;

    if (SetClientPropertyValueRequestPacket::create(m_clientId,
                                                    clientId,
                                                    getNextPacketId(),
                                                    propertyId,
                                                    value,
                                                    &packet) == false)
    {
        // Error, failed to create packet
        return false;
    }

    // Send packet
    if (sendPacket(packet) == false)
    {
        // Error, failed to send packet
        return false;
    }

    // Success
    return true;
}

bool DataBus::AbstractClient::sendSetClientPropertyValueResponsePacket(const quint8 clientId,
                                                                       const quint8 packetId,
                                                                       const quint8 propertyId,
                                                                       const quint8 returnStatus)
{
    // Create packet
    Packet packet;

    if (SetClientPropertyValueResponsePacket::create(m_clientId,
                                                     clientId,
                                                     packetId,
                                                     propertyId,
                                                     returnStatus,
                                                     &packet) == false)
    {
        // Error, failed to create packet
        return false;
    }

    // Send packet
    if (sendPacket(packet) == false)
    {
        // Error, failed to send packet
        return false;
    }

    // Success
    return true;
}

bool DataBus::AbstractClient::sendGetClientMethodListRequestPacket(const quint8 clientId)
{
    // Create packet
    Packet packet;

    if (GetClientMethodListRequestPacket::create(m_clientId, clientId, getNextPacketId(), &packet) == false)
    {
        // Error, failed to create packet
        return false;
    }

    // Send packet
    if (sendPacket(packet) == false)
    {
        // Error, failed to send packet
        return false;
    }

    // Success
    return true;
}

bool DataBus::AbstractClient::sendGetClientMethodInfoRequestPacket(const quint8 clientId,
                                                                   const quint8 methodId)
{
    // Create packet
    Packet packet;

    if (GetClientMethodInfoRequestPacket::create(m_clientId,
                                                 clientId,
                                                 getNextPacketId(),
                                                 methodId,
                                                 &packet) == false)
    {
        // Error, failed to create packet
        return false;
    }

    // Send packet
    if (sendPacket(packet) == false)
    {
        // Error, failed to send packet
        return false;
    }

    // Success
    return true;
}

bool DataBus::AbstractClient::sendGetClientMethodParameterInfoRequestPacket(const quint8 clientId,
                                                                            const quint8 methodId,
                                                                            const quint8 parameterIndex)
{
    // Create packet
    Packet packet;

    if (GetClientMethodParameterInfoRequestPacket::create(m_clientId,
                                                          clientId,
                                                          getNextPacketId(),
                                                          methodId,
                                                          parameterIndex,
                                                          &packet) == false)
    {
        // Error, failed to create packet
        return false;
    }

    // Send packet
    if (sendPacket(packet) == false)
    {
        // Error, failed to send packet
        return false;
    }

    // Success
    return true;
}

bool DataBus::AbstractClient::sendGetClientMethodReturnValueInfoRequestPacket(const quint8 clientId,
                                                                              const quint8 methodId,
                                                                              const quint8 returnValueIndex)
{
    // Create packet
    Packet packet;

    if (GetClientMethodParameterInfoRequestPacket::create(m_clientId,
                                                          clientId,
                                                          getNextPacketId(),
                                                          methodId,
                                                          returnValueIndex,
                                                          &packet) == false)
    {
        // Error, failed to create packet
        return false;
    }

    // Send packet
    if (sendPacket(packet) == false)
    {
        // Error, failed to send packet
        return false;
    }

    // Success
    return true;
}

// TODO: add other packets


bool DataBus::AbstractClient::start(const quint8 clientId,
                                    const QString &clientName,
                                    const quint8 activityTimeout,
                                    const quint8 pingResponseTimeout,
                                    const quint8 pingRetryCount,
                                    const int registrationTimeout)
{
    // Check input parameters
    if ((clientId == 0) ||
        (clientName.toUtf8().length() > STRING_MAX_LENGTH) ||
        (activityTimeout < 10) ||
        (pingResponseTimeout < 10) ||
        (pingResponseTimeout > 60) ||
        (pingResponseTimeout > activityTimeout) ||
        (registrationTimeout < 5000))
    {
        // Error, invalid input parameters
        return false;
    }

    // Initialize DataBus Packet parser
    m_packetParser.initialize();

    // Save client registration settings
    m_clientId = clientId;
    m_clientName = clientName;
    m_activityTimeout = activityTimeout;
    m_pingResponseTimeout = pingResponseTimeout;
    m_pingRetryCount = pingRetryCount;

    // Initialize Packet ID generator
    m_packetIdCounter = 0;

    // Initialize server timeout (2x activity timeout in milliseconds)
    m_serverTimeout = m_activityTimeout * 1000 * 2;

    // Start the registration process
    Packet packet;

    if (RegisterClientRequestPacket::create(m_clientId,
                                            getNextPacketId(),
                                            m_activityTimeout,
                                            m_pingResponseTimeout,
                                            m_pingRetryCount,
                                            &packet) == false)
    {
        // Error, failed to create packet
        return false;
    }

    if (sendPacket(packet) == false)
    {
        // Error, failed to send packet
        return false;
    }

    // Wait for client registration to finish
    m_timer.stop();

    connect(&m_timer, SIGNAL(timeout()), this, SLOT(processRegistrationTimeout()));

    m_timer.setSingleShot(true);
    m_timer.start(registrationTimeout);

    // Success
    return true;
}

void DataBus::AbstractClient::stop()
{
    // Stop timer
    m_timer.stop();

    // Initialize client to the default state
    m_packetParser.initialize();
    m_clientId = 0;
    m_clientName = QString();
    m_activityTimeout = 0;
    m_pingResponseTimeout = 0;
    m_pingRetryCount = 0;
    m_packetIdCounter = 0;
    m_serverTimeout = 0;

    // Unregister
    if (m_registered)
    {
        m_registered = false;

        // Send stopped signal
        emit stopped();
    }
}

quint8 DataBus::AbstractClient::getNextPacketId()
{
    return m_packetIdCounter++;
}

void DataBus::AbstractClient::processReceivedData(const QByteArray &receivedData)
{
    // Read available data and process it
    m_packetParser.processReceivedData(receivedData);

    // Check if new packet is available
    if (m_packetParser.isPacketAvailable() == false)
    {
        return;
    }

    // Process received packets
    processReceivedPackets();
}

QList<quint8> DataBus::AbstractClient::getPropertyIdList() const
{
    // Properties are disabled by default
    return QList<quint8>();
}

bool DataBus::AbstractClient::getPropertyInfo(const quint8, PropertyInfo *) const
{
    // Properties are disabled by default
    return false;
}

bool DataBus::AbstractClient::getPropertyValue(const quint8, Value *)
{
    // Properties are disabled by default
    return false;
}

bool DataBus::AbstractClient::setPropertyValue(const quint8, const Value &)
{
    // Properties are disabled by default
    return false;
}

QList<quint8> DataBus::AbstractClient::getMethodIdList() const
{
    // Methods are disabled by default
    return QList<quint8>();
}

bool DataBus::AbstractClient::getMethodInfo(const quint8, MethodInfo *) const
{
    // Methods are disabled by default
    return false;
}

bool DataBus::AbstractClient::getMethodParameterInfo(const quint8, const quint8, ValueInfo *) const
{
    // Methods are disabled by default
    return false;
}

bool DataBus::AbstractClient::getMethodReturnValueInfo(const quint8, const quint8, ValueInfo *) const
{
    // Methods are disabled by default
    return false;
}

void DataBus::AbstractClient::processActivity()
{
    // Check for server timeout
    if (m_serverTimeoutTimer.elapsed() >= m_serverTimeout)
    {
        // Server timeout occured, stop the client
        stop();
    }
}

void DataBus::AbstractClient::processRegistrationTimeout()
{
    // Process registration timeout only if the client registration failed. If client was registered just
    // before registration timeout occured then ignore it.
    if (m_registered == false)
    {
        stop();
        emit registered(false);
    }
}

void DataBus::AbstractClient::processReceivedPackets()
{
    // Check if a packet is available
    while (m_packetParser.isPacketAvailable())
    {
        // Process received packet
        Packet packet = m_packetParser.takePacket();

        switch (packet.getPacketType())
        {
            case PacketType_PingRequest:
            {
                processPingRequestPacket(packet);
                break;
            }

            case PacketType_RegisterClientResponse:
            {
                processRegisterClientResponsePacket(packet);
                break;
            }

            case PacketType_GetClientNameRequest:
            {
                processGetClientNameRequestPacket(packet);
                break;
            }

            case PacketType_GetClientPropertyListRequest:
            {
                processGetClientPropertyListRequestPacket(packet);
                break;
            }

            case PacketType_GetClientPropertyInfoRequest:
            {
                processGetClientPropertyInfoRequestPacket(packet);
                break;
            }

            case PacketType_GetClientPropertyValueRequest:
            {
                processGetClientPropertyValueRequestPacket(packet);
                break;
            }

            case PacketType_SetClientPropertyValueRequest:
            {
                processSetClientPropertyValueRequestPacket(packet);
                break;
            }

            case PacketType_GetClientMethodListRequest:
            {
                processGetClientMethodListRequestPacket(packet);
                break;
            }

            case PacketType_GetClientMethodInfoRequest:
            {
                processGetClientMethodInfoRequestPacket(packet);
                break;
            }

            case PacketType_GetClientMethodParameterInfoRequest:
            {
                processGetClientMethodParameterInfoRequestPacket(packet);
                break;
            }

            case PacketType_GetClientMethodReturnValueInfoRequest:
            {
                processGetClientMethodReturnValueInfoRequestPacket(packet);
                break;
            }

                // TODO:

            default:
            {
                // Check if registered
                if (m_registered == false)
                {
                    // Ignore received packets when client is not registered
                    break;
                }

                // Forward this to packet to outside
                processReceivedPacket(packet);
                break;
            }
        }
    }
}

void DataBus::AbstractClient::processPingRequestPacket(const Packet &packet)
{
    // Check if registered
    if (m_registered == false)
    {
        // Error, can't send a response if not registered
        return;
    }

    // Check destination
    if (packet.getDestination() != m_clientId)
    {
        // Error, invalid destination
        return;
    }

    // Create response
    Packet responsePacket;

    if (PingResponsePacket::create(m_clientId,
                                   packet.getPacketId(),
                                   &responsePacket) == false)
    {
        // Error, failed to create packet
        return;
    }

    // Send Response
    if (sendPacket(responsePacket) == false)
    {
        // Error, failed to send packet
        return;
    }

    // Restart server timeout timer
    m_serverTimeoutTimer.start();
}

void DataBus::AbstractClient::processRegisterClientResponsePacket(const Packet &packet)
{
    // Check if registered
    if (m_registered)
    {
        // Error, already registered
        return;
    }

    // Check destination
    if (packet.getDestination() != m_clientId)
    {
        // Error, invalid destination
        stop();
        emit registered(false);
        return;
    }

    // Stop registration timer
    m_timer.stop();
    m_timer.disconnect();

    // Check return status
    quint8 returnStatus;

    if (RegisterClientResponsePacket::parse(packet,
                                            &returnStatus) == false)
    {
        // Error, failed to parse packet
        stop();
        emit registered(false);
        return;
    }

    if (returnStatus != RETURN_STATUS_SUCCESS)
    {
        // Error, failed to parse packet
        stop();
        emit registered(false);
        return;
    }

    // Connect server activity timer signal
    connect(&m_timer, SIGNAL(timeout()), this, SLOT(processActivity()));

    // Activate client
    m_serverTimeoutTimer.start();

    // Start activity timer
    m_timer.setSingleShot(false);
    m_timer.start(1000);

    // Register client
    m_registered = true;

    // Send registered successully signal
    emit registered(true);
}

void DataBus::AbstractClient::processGetClientNameRequestPacket(const Packet &packet)
{
    // Check if registered
    if (m_registered == false)
    {
        // Error, can't send a response if not registered
        return;
    }

    // Check destination
    if (packet.getDestination() != m_clientId)
    {
        // Error, invalid destination
        return;
    }

    // Create response
    Packet responsePacket;

    if (GetClientNameResponsePacket::create(m_clientId,
                                            packet.getSource(),
                                            packet.getPacketId(),
                                            m_clientName,
                                            &responsePacket) == false)
    {
        // Error, failed to create packet
        return;
    }

    // Send Response
    if (sendPacket(responsePacket) == false)
    {
        // Error, failed to create packet
        return;
    }

    // Success
    return;
}

void DataBus::AbstractClient::processGetClientPropertyListRequestPacket(const Packet &packet)
{
    // Check if registered
    if (m_registered == false)
    {
        // Error, can't send a response if not registered
        return;
    }

    // Check destination
    if (packet.getDestination() != m_clientId)
    {
        // Error, invalid destination
        return;
    }

    // Get Property ID list
    const QList<quint8> propertyIdList = getPropertyIdList();

    // Create response
    Packet responsePacket;

    if (GetClientPropertyListResponsePacket::create(m_clientId,
                                                    packet.getSource(),
                                                    packet.getPacketId(),
                                                    propertyIdList,
                                                    &responsePacket) == false)
    {
        // Error, failed to create packet
        return;
    }

    // Send Response
    if (sendPacket(responsePacket) == false)
    {
        // Error, failed to create packet
        return;
    }

    // Success
    return;
}

void DataBus::AbstractClient::processGetClientPropertyInfoRequestPacket(const Packet &packet)
{
    // Check if registered
    if (m_registered == false)
    {
        // Error, can't send a response if not registered
        return;
    }

    // Check destination
    if (packet.getDestination() != m_clientId)
    {
        // Error, invalid destination
        return;
    }

    // Get Property ID
    quint8 propertyId;

    if (GetClientPropertyInfoRequestPacket::parse(packet,
                                                  &propertyId) == false)
    {
        // Error, failed to parse packet
        return;
    }

    // Get Property Info
    PropertyInfo propertyInfo;

    if (getPropertyInfo(propertyId, &propertyInfo) == false)
    {
        // Error, failed to get Property Info
        return;
    }

    // Create response
    Packet responsePacket;

    if (GetClientPropertyInfoResponsePacket::create(m_clientId,
                                                    packet.getSource(),
                                                    packet.getPacketId(),
                                                    propertyInfo,
                                                    &responsePacket) == false)
    {
        // Error, failed to create packet
        return;
    }

    // Send Response
    if (sendPacket(responsePacket) == false)
    {
        // Error, failed to create packet
        return;
    }

    // Success
    return;
}

void DataBus::AbstractClient::processGetClientPropertyValueRequestPacket(const Packet &packet)
{
    // Check if registered
    if (m_registered == false)
    {
        // Error, can't send a response if not registered
        return;
    }

    // Check destination
    if (packet.getDestination() != m_clientId)
    {
        // Error, invalid destination
        return;
    }

    // Get Property ID
    quint8 propertyId;

    if (GetClientPropertyValueRequestPacket::parse(packet,
                                                   &propertyId) == false)
    {
        // Error, failed to parse packet
        return;
    }

    // Get Property Value
    Value propertyValue;

    if (getPropertyValue(propertyId, &propertyValue) == false)
    {
        // Error, failed to get property value
        return;
    }

    // Create response
    Packet responsePacket;

    if (GetClientPropertyValueResponsePacket::create(m_clientId,
                                                     packet.getSource(),
                                                     packet.getPacketId(),
                                                     propertyId,
                                                     propertyValue,
                                                     &responsePacket) == false)
    {
        // Error, failed to create packet
        return;
    }

    // Send Response
    if (sendPacket(responsePacket) == false)
    {
        // Error, failed to create packet
        return;
    }

    // Success
    return;
}

void DataBus::AbstractClient::processSetClientPropertyValueRequestPacket(const Packet &packet)
{
    // Check if registered
    if (m_registered == false)
    {
        // Error, can't send a response if not registered
        return;
    }

    // Check destination
    if (packet.getDestination() != m_clientId)
    {
        // Error, invalid destination
        return;
    }

    // Get Property ID and Value
    quint8 propertyId;
    Value propertyValue;

    if (SetClientPropertyValueRequestPacket::parse(packet,
                                                   &propertyId,
                                                   &propertyValue) == false)
    {
        // Error, failed to parse packet
        return;
    }

    // Set Property Value
    quint8 returnStatus = RETURN_STATUS_SUCCESS;

    if (setPropertyValue(propertyId, propertyValue) == false)
    {
        // Error, failed to get property value
        returnStatus = RETURN_STATUS_INVALID_PROPERTY_ID;
    }

    // Create response
    Packet responsePacket;

    if (SetClientPropertyValueResponsePacket::create(m_clientId,
                                                     packet.getSource(),
                                                     packet.getPacketId(),
                                                     propertyId,
                                                     returnStatus,
                                                     &responsePacket) == false)
    {
        // Error, failed to create packet
        return;
    }

    // Send Response
    if (sendPacket(responsePacket) == false)
    {
        // Error, failed to create packet
        return;
    }

    // Success
    return;
}

void DataBus::AbstractClient::processGetClientMethodListRequestPacket(const Packet &packet)
{
    // Check if registered
    if (m_registered == false)
    {
        // Error, can't send a response if not registered
        return;
    }

    // Check destination
    if (packet.getDestination() != m_clientId)
    {
        // Error, invalid destination
        return;
    }

    // Get Method ID list
    const QList<quint8> methodIdList = getMethodIdList();

    // Create response
    Packet responsePacket;

    if (GetClientMethodListResponsePacket::create(m_clientId,
                                                  packet.getSource(),
                                                  packet.getPacketId(),
                                                  methodIdList,
                                                  &responsePacket) == false)
    {
        // Error, failed to create packet
        return;
    }

    // Send Response
    if (sendPacket(responsePacket) == false)
    {
        // Error, failed to create packet
        return;
    }

    // Success
    return;
}

void DataBus::AbstractClient::processGetClientMethodInfoRequestPacket(const Packet &packet)
{
    // Check if registered
    if (m_registered == false)
    {
        // Error, can't send a response if not registered
        return;
    }

    // Check destination
    if (packet.getDestination() != m_clientId)
    {
        // Error, invalid destination
        return;
    }

    // Get Method ID
    quint8 methodId;

    if (GetClientMethodInfoRequestPacket::parse(packet,
                                                &methodId) == false)
    {
        // Error, failed to parse packet
        return;
    }

    // Get Method Info
    MethodInfo methodInfo;

    if (getMethodInfo(methodId, &methodInfo) == false)
    {
        // Error, failed to get Method Info
        return;
    }

    // Create response
    Packet responsePacket;

    if (GetClientMethodInfoResponsePacket::create(m_clientId,
                                                  packet.getSource(),
                                                  packet.getPacketId(),
                                                  methodInfo,
                                                  &responsePacket) == false)
    {
        // Error, failed to create packet
        return;
    }

    // Send Response
    if (sendPacket(responsePacket) == false)
    {
        // Error, failed to create packet
        return;
    }

    // Success
    return;
}

void DataBus::AbstractClient::processGetClientMethodParameterInfoRequestPacket(const Packet &packet)
{
    // Check if registered
    if (m_registered == false)
    {
        // Error, can't send a response if not registered
        return;
    }

    // Check destination
    if (packet.getDestination() != m_clientId)
    {
        // Error, invalid destination
        return;
    }

    // Get Method ID and Parameter Index
    quint8 methodId;
    quint8 parameterIndex;

    if (GetClientMethodParameterInfoRequestPacket::parse(packet,
                                                         &methodId,
                                                         &parameterIndex) == false)
    {
        // Error, failed to parse packet
        return;
    }

    // Get Parameter Info
    ValueInfo parameterInfo;

    if (getMethodParameterInfo(methodId, parameterIndex, &parameterInfo) == false)
    {
        // Error, failed to get Parameter Info
        return;
    }

    // Create response
    Packet responsePacket;

    if (GetClientMethodParameterInfoResponsePacket::create(m_clientId,
                                                           packet.getSource(),
                                                           packet.getPacketId(),
                                                           methodId,
                                                           parameterIndex,
                                                           parameterInfo,
                                                           &responsePacket) == false)
    {
        // Error, failed to create packet
        return;
    }

    // Send Response
    if (sendPacket(responsePacket) == false)
    {
        // Error, failed to create packet
        return;
    }

    // Success
    return;
}

void DataBus::AbstractClient::processGetClientMethodReturnValueInfoRequestPacket(const Packet &packet)
{
    // Check if registered
    if (m_registered == false)
    {
        // Error, can't send a response if not registered
        return;
    }

    // Check destination
    if (packet.getDestination() != m_clientId)
    {
        // Error, invalid destination
        return;
    }

    // Get Method ID and Return Value Index
    quint8 methodId;
    quint8 returnValueIndex;

    if (GetClientMethodReturnValueInfoRequestPacket::parse(packet,
                                                           &methodId,
                                                           &returnValueIndex) == false)
    {
        // Error, failed to parse packet
        return;
    }

    // Get Return Value Info
    ValueInfo returnValueInfo;

    if (getMethodReturnValueInfo(methodId, returnValueIndex, &returnValueInfo) == false)
    {
        // Error, failed to get Return Value Info
        return;
    }

    // Create response
    Packet responsePacket;

    if (GetClientMethodReturnValueInfoResponsePacket::create(m_clientId,
                                                             packet.getSource(),
                                                             packet.getPacketId(),
                                                             methodId,
                                                             returnValueIndex,
                                                             returnValueInfo,
                                                             &responsePacket) == false)
    {
        // Error, failed to create packet
        return;
    }

    // Send Response
    if (sendPacket(responsePacket) == false)
    {
        // Error, failed to create packet
        return;
    }

    // Success
    return;
}
